package com.example.origin.finance.trade.service.impl;


import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.example.origin.finance.dto.*;
import com.example.origin.finance.enums.TableNameEnum;
import com.example.origin.finance.trade.dto.InfoResource;
import com.example.origin.finance.trade.dto.Trader;
import com.example.origin.finance.trade.dto.impl.BlueAnalyst;
import com.example.origin.finance.trade.dto.impl.GreenAnalyst;
import com.example.origin.finance.trade.service.AbstractModelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AlphaModelService extends AbstractModelService {

    public void model(String nowStr, String endStr) {
        DateTime now = DateUtil.parseDate(nowStr);
        DateTime start = DateUtil.offset(now, DateField.YEAR, -7);
        DateTime end = DateUtil.parseDate(endStr);
        Query query = new Query();
        query.addCriteria(Criteria.where("announcementTime").gte(now).lt(end));

        List<CnInfoReport> cnInfoReports = find(query, TableNameEnum.CNINFO_GUPIAO_REPORT);
        Map<String, Map<String, List<String>>> cnInfoReportsMap = cnInfoReports.stream().filter(e -> e.getShortTitle().contains("年")).collect(Collectors.groupingBy(e -> DateUtil.formatDate(e.getAnnouncementTime()), Collectors.groupingBy(BaseTable::getCode, Collectors.mapping(e -> {
            String year = DateUtil.format(e.getAnnouncementTime(), "yyyy");
            if (e.getShortTitle().contains("一季") || e.getShortTitle().contains("1季")) {
                return year + "-03-31";
            } else if (e.getShortTitle().contains("半年") || e.getShortTitle().contains("中期") || e.getShortTitle().contains("半度")) {
                return year + "-06-30";
            } else if (e.getShortTitle().contains("三季") || e.getShortTitle().contains("3季")) {
                return year + "-09-30";
            } else if ((e.getShortTitle().contains("年度") || e.getShortTitle().contains("年报")) && !e.getShortTitle().contains("半年")) {
                DateTime offset = DateUtil.offset(e.getAnnouncementTime(), DateField.YEAR, -1);
                String yyyy = DateUtil.format(offset, "yyyy");
                return yyyy + "-12-31";
            } else {
                System.out.println(e.getShortTitle());
                throw new RuntimeException();
            }
        }, Collectors.toList()))));
        // quote
        query = new Query();
        List<XueqiuQuote> xueqiuQuotes = find(query, TableNameEnum.XUEQIU_GUPIAO_QUOTE);
        Map<String, XueqiuQuote> quoteMap = xueqiuQuotes.stream().collect(Collectors.toMap(BaseTable::getCode, Function.identity()));

        // all
        query = new Query();
        query.addCriteria(Criteria.where("reportDate").gte(start).lt(end));

        List<XueqiuIndicator> xueqiuIndicators = find(query, TableNameEnum.XUEQIU_GUPIAO_INDICATOR);
        Map<String, Map<String, List<XueqiuIndicator>>> xueqiuIndicatorsMMddMap = xueqiuIndicators.stream().collect(Collectors.groupingBy(BaseTable::getCode, Collectors.groupingBy(e -> DateUtil.format(e.getReportDate(), "MM-dd"))));

        List<XueqiuCashFlow> xueqiuCashFlows = find(query, TableNameEnum.XUEQIU_GUPIAO_CASH_FLOW);
        Map<String, Map<String, List<XueqiuCashFlow>>> xueqiuCashFlowsMMddMap = xueqiuCashFlows.stream().collect(Collectors.groupingBy(BaseTable::getCode, Collectors.groupingBy(e -> DateUtil.format(e.getReportDate(), "MM-dd"))));


        // kline
        query = new Query();
        query.addCriteria(Criteria.where("timestamp").gte(now).lt(end));

        List<XueqiuKline> xueqiuKlines = find(query, TableNameEnum.XUEQIU_GUPIAO_KLINE);
        filterKline(xueqiuKlines);
        Map<String, Map<String, XueqiuKline>> xueqiuKlinesMap = xueqiuKlines.stream().collect(Collectors.groupingBy(e -> DateUtil.formatDate(e.getTimestamp()), Collectors.toMap(BaseTable::getCode, e -> e)));

        Trader trader = new Trader();
        trader.setAnalyst(new GreenAnalyst());
        while (DateUtil.compare(now, end) < 0) {
            String nowDayStr = DateUtil.formatDate(now);
            Map<String, List<String>> listMap = cnInfoReportsMap.get(nowDayStr);
            if (listMap != null) {

                for (Map.Entry<String, List<String>> listEntry : listMap.entrySet()) {
                    String code = listEntry.getKey();
                    String nextDayStr = getNextDayStr(nowDayStr, xueqiuKlinesMap);
                    if (!klineExist(xueqiuKlinesMap, nextDayStr)) {
                        continue;
                    }
                    Map<String, XueqiuKline> xueqiuKlineMap = xueqiuKlinesMap.get(nextDayStr);
                    XueqiuKline xueqiuKline = xueqiuKlineMap.get(code);
                    if (xueqiuKline == null) {
                        continue;
                    }

                    if (!quoteMap.containsKey(code)) {
                        continue;
                    }

                    InfoResource infoResource = new InfoResource();
                    infoResource.setCode(code);
                    infoResource.setTime(xueqiuKline.getTimestamp());
                    infoResource.setKline(xueqiuKline);
                    infoResource.setQuote(quoteMap.get(code));

                    List<String> reportDates = listEntry.getValue();
                    reportDates.sort(Comparator.comparing(e -> e, Comparator.reverseOrder()));
                    String reportDate = reportDates.get(0);
                    DateTime dateTime = DateUtil.parseDate(reportDate);
                    if (xueqiuCashFlowsMMddMap.containsKey(code)) {
                        if (xueqiuCashFlowsMMddMap.get(code).containsKey(DateUtil.format(dateTime, "MM-dd"))) {
                            List<XueqiuCashFlow> cashFlows = xueqiuCashFlowsMMddMap.get(code).get(DateUtil.format(dateTime, "MM-dd"));
                            infoResource.setMoreCashFlows(cashFlows.stream().filter(e -> DateUtil.compare(e.getReportDate(), dateTime) <= 0).collect(Collectors.toList()));
                            Map<String, XueqiuCashFlow> cashFlowMap = cashFlows.stream().collect(Collectors.toMap(e -> DateUtil.formatDate(e.getReportDate()), Function.identity(), (a, b) -> a));
                            XueqiuCashFlow xueqiuCashFlow = cashFlowMap.get(reportDate);
                            if (xueqiuCashFlow == null) {
                                continue;
                            }
                            XueqiuCashFlow preXueqiuCashFlow = cashFlowMap.get(DateUtil.formatDate(DateUtil.offset(DateUtil.parseDate(reportDate), DateField.YEAR, -1)));
                            if (preXueqiuCashFlow == null) {
                                continue;
                            }
                            infoResource.setTwoCashFlows(Arrays.asList(xueqiuCashFlow, preXueqiuCashFlow));
                        }
                    }
                    if (xueqiuIndicatorsMMddMap.containsKey(code)) {
                        if (xueqiuIndicatorsMMddMap.get(code).containsKey(DateUtil.format(dateTime, "MM-dd"))) {
                            List<XueqiuIndicator> indicators = xueqiuIndicatorsMMddMap.get(code).get(DateUtil.format(dateTime, "MM-dd"));
                            infoResource.setMoreIndicators(indicators.stream().filter(e -> DateUtil.compare(e.getReportDate(), dateTime) <= 0).collect(Collectors.toList()));
                            Map<String, XueqiuIndicator> indicatorMap = indicators.stream().collect(Collectors.toMap(e -> DateUtil.formatDate(e.getReportDate()), Function.identity(), (a, b) -> a));
                            XueqiuIndicator xueqiuIndicator = indicatorMap.get(reportDate);
                            if (xueqiuIndicator == null) {
                                continue;
                            }
                            XueqiuIndicator preXueqiuIndicator = indicatorMap.get(DateUtil.formatDate(DateUtil.offset(DateUtil.parseDate(reportDate), DateField.YEAR, -1)));
                            if (preXueqiuIndicator == null) {
                                continue;
                            }
                            infoResource.setTwoIndicators(Arrays.asList(xueqiuIndicator, preXueqiuIndicator));
                        }
                    }

                    trader.getInfoResources().add(infoResource);
                    trader.buyOrSell();


                }
            }
            if (klineExist(xueqiuKlinesMap, DateUtil.formatDate(now))) {
                trader.logAsset(now, xueqiuKlinesMap.get(DateUtil.formatDate(now)));
            }
            now = DateUtil.offsetDay(now, 1);
        }
        while (!klineExist(xueqiuKlinesMap, DateUtil.formatDate(now))) {
            now = DateUtil.offsetDay(now, -1);
        }
        Map<String, XueqiuKline> klinesMap = xueqiuKlinesMap.get(DateUtil.formatDate(now));
        trader.allSell(now, klinesMap);


    }

    private static String getNextDayStr(String nowDayStr, Map<String, Map<String, XueqiuKline>> xueqiuKlinesMap) {
        String nextDayStr = nowDayStr;
        int count = 0;
        while (!klineExist(xueqiuKlinesMap, nextDayStr)) {
            DateTime nextDay = DateUtil.offsetDay(DateUtil.parseDate(nextDayStr), 1);
            nextDayStr = DateUtil.formatDate(nextDay);
            if (count > 10) {
                break;
            }
            count++;
        }
        return nextDayStr;
    }

    private static boolean klineExist(Map<String, Map<String, XueqiuKline>> xueqiuKlinesMap, String nextDayStr) {
        return xueqiuKlinesMap.containsKey(nextDayStr) && xueqiuKlinesMap.get(nextDayStr).size() > 100;
    }
}
